%%%----------------------------------------------------------------------
%%% File    : cus_ejabberd_c2s.erl
%%% Author  : chen.kangmin
%%%----------------------------------------------------------------------

-module(cus_util).
-author('chen.kangmin').

-include("jlib.hrl").
-include("cus.hrl").


-export([
    to_list/1,
    str_to_number/1,
    hex_str/1,
    hmac_sha/2,
    err/2,
    new_table/3,
    decode_private_key/1,
    decrypt_private/2,
    aes_cfb_128_decrypt/2,
    aes_cfb_128_encrypt/2,
    pkcs5_padding/2,
    pkcs5_unpadding/2,
    utc/0,
    utc_str/0,
    utc_timestamp_str/0,
    utc_to_datetime/1,
    datetime_to_str/1,
    is_backend_user/1,
    conversation_type/1,
    get_quan_id_from_cid/1,
    get_users_from_cid/1,
    get_cid_from_mid/1,
    random/1,
    now/0,
    join_cluster/1,
    term_to_bitstring/1,
    bitstring_to_term/1,
    uuid/0,
    gc/0,
    phash/2,
    make_jid/1,
    gmt_str/0,
    get_idx/2,
    http_request/2
        ]).


-define(UTC_POINT, {{1970,1,1}, {0,0,0}}).


to_list(Prop) ->
    case Prop of
        undefined ->
            "";
        B when is_binary(B) ->
            binary_to_list(B);
        I when is_integer(I) ->
            integer_to_list(I);
        F when is_float(F) ->
            lists:flatten(io_lib:format("~.3f", [F]));
        A when is_atom(A) ->
            atom_to_list(A);
        L when is_list(L) ->
            L;
        _ ->
            Prop
    end.


str_to_number(Str) ->
    try
        case string:chr(Str, $.) > 0 of
            true  -> list_to_float(Str);
            false -> list_to_integer(Str)
        end
    catch
        _Type:_Any -> undefined
    end.


hex_str(S) when is_binary(S) ->
    hex_str(to_list(S));
hex_str(S) ->
    hex_1(S, []).

hex_1([], Res) ->
    lists:reverse(Res);
hex_1([N | Ns], Res) ->
    hex_1(Ns, [digit_to_xchar(N rem 16),
               digit_to_xchar(N div 16) | Res]).


digit_to_xchar(D) when (D >= 0) and (D < 10) -> D + 48;
digit_to_xchar(D) -> D + 87.


hmac_sha(PlainText, Key) when is_list(Key) ->
    crypto:hmac(sha, list_to_binary(Key), list_to_binary(PlainText));
hmac_sha(PlainText, Key) when is_binary(Key) ->
    crypto:hmac(sha, Key, list_to_binary(PlainText)).


err(CODE, MSG) ->
    throw({CODE, MSG}).


new_table(TableName, Opts, Copy) ->
    case mnesia:create_table(TableName, Opts) of
        {aborted, {already_exists, _}} ->
            mnesia:add_table_copy(TableName, node(), Copy);
        _ ->
            skip
    end.


decode_private_key(File) ->
    {ok, Content} = file:read_file(File),
    [KeyInfo] = public_key:pem_decode(Content),
    public_key:pem_entry_decode(KeyInfo).


decrypt_private(Cipher, Key) ->
    public_key:decrypt_private(Cipher, Key).


aes_cfb_128_decrypt(Cipher, Key) ->
    pkcs5_unpadding(crypto:aes_cbc_128_decrypt(Key, ?IV, Cipher), 16).


aes_cfb_128_encrypt(PlainText, Key) ->
    crypto:aes_cbc_128_encrypt(Key, ?IV, pkcs5_padding(PlainText, 16)).


pkcs5_padding(PlainText, BlockSize) when is_binary(PlainText) ->
    Rem = size(PlainText) rem BlockSize,
    Padding = lists:duplicate(BlockSize - Rem, BlockSize - Rem),
    Binary = list_to_binary(Padding),
    <<PlainText/binary, Binary/binary>>;
pkcs5_padding(PlainText, BlockSize) when is_list(PlainText) ->
    Rem = length(PlainText) rem BlockSize,
    Padding = lists:duplicate(BlockSize - Rem, BlockSize - Rem),
    PlainText ++ Padding.

pkcs5_unpadding(PlainText, _BlockSize) when is_binary(PlainText) ->
    TextSize = size(PlainText) ,
    Size = TextSize -1,
    <<_:Size/binary, PaddingSize>> = PlainText,
    PreSize = TextSize - PaddingSize,
    <<Pre:PreSize/binary, _/binary>> = PlainText,
    Pre;
pkcs5_unpadding(PlainText, _BlockSize) when is_list(PlainText) ->
    PaddingSize = lists:last(PlainText),
    {Pre, _} = lists:split(length(PlainText) - PaddingSize, PlainText),
    Pre.


utc_point() ->
    calendar:datetime_to_gregorian_seconds(?UTC_POINT).

utc() ->
    {MegaSecs, Secs, _MicroSecs} = os:timestamp(),
    MegaSecs * 1000000 + Secs.


utc_str() ->
    {MegaSecs, Secs, _MicroSecs} = os:timestamp(),
    to_list(MegaSecs * 1000000 + Secs).


utc_timestamp_str() ->
    {MegaSecs, Secs, MicroSecs} = os:timestamp(),
    to_list((MegaSecs * 1000000 + Secs) * 1000 + (MicroSecs div 1000)).


utc_to_datetime(Seconds) ->
    calendar:now_to_universal_time({Seconds div 1000000, Seconds rem 1000000, 0}).


datetime_to_str({{Year, Month, Day}, {Hour, Min, Sec}}) ->
    lists:flatten(io_lib:format(
        "~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B",
        [Year, Month, Day, Hour, Min, Sec])).


get_quan_id_from_cid([$Q, $_ | Rest]) ->
    Rest;
get_quan_id_from_cid([$T, $_ | Rest]) ->
    [QuanId, _] = re:split(Rest, "_", [{return, list}]),
    QuanId;
get_quan_id_from_cid([$F, $_ | Rest]) ->
    [QuanId, _Uid1, _Uid2] = re:split(Rest, "_", [{return, list}]),
    QuanId.
    

get_users_from_cid([$F, $_ | Rest]) ->
    [_QuanId, Uid1, Uid2] = re:split(Rest, "_", [{return, list}]),
    [Uid1, Uid2].


get_cid_from_mid(Mid) ->
    List = re:split(Mid, "_", [{return, list}]),
    Seq = lists:last(List),
    string:join(lists:delete(Seq, List), "_").


conversation_type([$Q, $_ | _Rest]) ->   %%圈子会话id规则：Q_圈子id
    ?QUANLIAO;
conversation_type([$T, $_ | _Rest]) ->   %%讨论组会话id规则：T_圈子id_md5(uid+当前时间)
    ?TAOLUNZU;
conversation_type([$F, $_ | _Rest]) ->   %%单聊会话id规则：F_圈子id_uid_uid
    ?DANLIAO.


is_backend_user([$b, $a, $c, $k, $e, $n, $d, $_ | _]) ->
    true;
is_backend_user(_) ->
    false.


random(Num) ->
    {A, B, C} = os:timestamp(),
    random:seed(A, B, C),
    random:uniform(Num).



join_cluster(NodeName) ->
    mnesia:stop(),
    mnesia:delete_schema([node()]),
    mnesia:start(),
    mnesia:change_config(extra_db_nodes, [NodeName]),
    mnesia:change_table_copy_type(schema, node(), disc_copies).


term_to_bitstring(Term) ->
    erlang:list_to_bitstring(io_lib:format("~p", [Term])).


bitstring_to_term(BitString) ->
    string_to_term(binary_to_list(BitString)).


string_to_term(String) ->
    case erl_scan:string(String++".") of
        {ok, Tokens, _} ->
            case catch erl_parse:parse_term(Tokens) of
                {ok, Term} -> Term;
                _Err -> undefined
            end;
        _Error ->
            undefined
    end.

now() ->
    os:timestamp().


uuid() ->
    <<A:32, B:16, C:16, D:16, E:48>> = crypto:rand_bytes(16),
    Str = io_lib:format("~8.16.0b-~4.16.0b-4~3.16.0b-~4.16.0b-~12.16.0b",
                        [A, B, C band 16#0fff, D band 16#3fff bor 16#8000, E]),
    binary_to_list(list_to_binary(Str)).

gc() ->
    [erlang:garbage_collect(P) || P <- erlang:processes()],
    erlang:garbage_collect().


phash(Key, Size) when is_list(Key) ->
    phash(?l2b(Key), Size);
phash(Key, Size) ->
    Size1 = size(Key) * 8,
    <<Num : Size1>> = Key,
    if
        Size > 1 ->
            (Num rem Size) + 1;
        true ->
            1
    end.


make_jid(Uid) when is_list(Uid) ->
    make_jid(?l2b(Uid));
make_jid(Uid) ->
    #jid{user = Uid, server = ?SERVER, resource = ?RESOURCE,
         luser = Uid, lserver = ?SERVER, lresource = ?RESOURCE}.


gmt_str() ->
    {{Year, Month, Day}, {Hour, Min, Sec}} = {Date, _Time} = calendar:local_time_to_universal_time(calendar:local_time()),
    Week = day(calendar:day_of_the_week(Date)),
    MonthStr = month(Month),
    lists:flatten(io_lib:format(
        "~s, ~2.10.0B ~s ~4.10.0B ~2.10.0B:~2.10.0B:~2.10.0B GMT",
        [Week, Day, MonthStr, Year, Hour, Min, Sec])).


day(1) -> "Mon";
day(2) -> "Tue";
day(3) -> "Wed";
day(4) -> "Thu";
day(5) -> "Fri";
day(6) -> "Sat";
day(7) -> "Sun".

month(1)  -> "Jan";
month(2)  -> "Feb";
month(3)  -> "Mar";
month(4)  -> "Apr";
month(5)  -> "May";
month(6)  -> "Jun";
month(7)  -> "Jul";
month(8)  -> "Aug";
month(9)  -> "Sep";
month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".


get_idx(Ele, List) ->
    get_idx(Ele, List, 1).

get_idx(_Ele, [], _Idx) ->
    0;
get_idx(Ele, [Item | T], Idx) ->
    case Ele of
        Item ->
            Idx;
        _ ->
            get_idx(Ele, T, Idx + 1)
    end.


http_request(Url, Params) ->
    case httpc:request(post, {Url, [], "application/x-www-form-urlencoded" , Params}, [{timeout, 60000}], []) of
        {ok, {_, _, ResBody}} ->
            ResBody;
        {error, Reason} ->
            {error, Reason}
    end.
